﻿//*****************************************************************************
// Source: http://www.codeproject.com/KB/WF/DynamicSendActivity.aspx
//*****************************************************************************
//
using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Activities;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.Text;
using System.Windows;
using System.Windows.Media;
using System.Xaml;
using System.Xml;
using System.Xml.Linq;

namespace TfsCloudDeploy.Workflow.Core
{
    public interface IForFactoryOnly
    {
    }

    public enum MessageVersionOption
    {
        Default,
        None,
        Soap11,
        Soap11WSAddressing10,
        Soap11WSAddressingAugust2004,
        Soap12,
        Soap12WSAddressing10,
        Soap12WSAddressingAugust2004,
        Unknown
    }

    public delegate void SelectEventHandler(object sender, SelectEventArgs e);
    public class SelectEventArgs : EventArgs
    {
        public string Name { get; set; }
        public string Topic { get; set; }
        public string Key { get; set; }
        public string Contract { get; set; }
        public string Operation { get; set; }
        public string Schema { get; set; }
        public string Message { get; set; }
        public object Tag { get; set; }
    }


    public class MessageHelper
    {
        public static string ToString(Message message)
        {
            StringWriter sw = new StringWriter();

            using (MessageBuffer buffer = message.CreateBufferedCopy(Int32.MaxValue))
            using (XmlTextWriter wr = new XmlTextWriter(sw) { Formatting = Formatting.Indented })
            using (XmlDictionaryWriter dwr = XmlDictionaryWriter.CreateDictionaryWriter(wr))
            {
                dwr.WriteStartDocument();
                buffer.CreateMessage().WriteMessage(dwr);
                dwr.WriteEndDocument();
                dwr.Flush();

                message = buffer.CreateMessage();

            }
            return sw.ToString();
        }

        public static XElement ToXml(MessageBuffer buffer)
        {
            using (MemoryStream ms = new MemoryStream())
            using (XmlTextWriter wr = new XmlTextWriter(ms, Encoding.ASCII))
            using (XmlDictionaryWriter dwr = XmlDictionaryWriter.CreateDictionaryWriter(wr))
            {
                dwr.WriteStartDocument();
                buffer.CreateMessage().WriteMessage(dwr);
                dwr.WriteEndDocument();
                dwr.Flush();
                ms.Position = 0;
                return XElement.Load(ms);
            }
        }

        public static XElement ToXml(Message message)
        {
            return XElement.Parse(message.ToString());
        }

        public static Message Copy(ref Message message)
        {
            if (message == null)
                return null;

            using (var buffer = message.CreateBufferedCopy(Int32.MaxValue))
            {
                message = buffer.CreateMessage();
                return buffer.CreateMessage();
            }
        }

        public static Message Copy(MessageBuffer buffer)
        {
            return buffer == null ? null : buffer.CreateMessage();
        }
    }

    public class LibHelper
    {
        public static MessageVersion MessageVersion(MessageVersionOption version)
        {
            return MessageVersion(version.ToString());
        }
        public static MessageVersionOption MessageVersion(System.ServiceModel.Channels.MessageVersion version)
        {
            if (version == null)
                return MessageVersionOption.Unknown;

            if (version == System.ServiceModel.Channels.MessageVersion.Soap11WSAddressing10)
                return MessageVersionOption.Soap11WSAddressing10;

            if (version == System.ServiceModel.Channels.MessageVersion.Soap12WSAddressing10)
                return MessageVersionOption.Soap12WSAddressing10;

            if (version == System.ServiceModel.Channels.MessageVersion.Soap11WSAddressingAugust2004)
                return MessageVersionOption.Soap11WSAddressingAugust2004;

            if (version == System.ServiceModel.Channels.MessageVersion.Soap12WSAddressingAugust2004)
                return MessageVersionOption.Soap12WSAddressingAugust2004;

            if (version == System.ServiceModel.Channels.MessageVersion.Soap11)
                return MessageVersionOption.Soap11;

            if (version == System.ServiceModel.Channels.MessageVersion.Soap12)
                return MessageVersionOption.Soap12;

            if (version == System.ServiceModel.Channels.MessageVersion.None)
                return MessageVersionOption.None;

            if (version == System.ServiceModel.Channels.MessageVersion.Default)
                return MessageVersionOption.Default;

            throw new ArgumentException("version");
        }
        public static MessageVersion MessageVersion(string version)
        {
            switch (version)
            {
                case "Soap11WSAddressing10":
                    return System.ServiceModel.Channels.MessageVersion.Soap11WSAddressing10;

                case "Soap12WSAddressing10":
                    return System.ServiceModel.Channels.MessageVersion.Soap12WSAddressing10;

                case "Soap11WSAddressingAugust2004":
                    return System.ServiceModel.Channels.MessageVersion.Soap11WSAddressingAugust2004;

                case "Soap12WSAddressingAugust2004":
                    return System.ServiceModel.Channels.MessageVersion.Soap12WSAddressingAugust2004;

                case "Soap11":
                    return System.ServiceModel.Channels.MessageVersion.Soap11;

                case "Soap12":
                    return System.ServiceModel.Channels.MessageVersion.Soap12;

                case "None":
                    return System.ServiceModel.Channels.MessageVersion.None;

                case "Default":
                    return System.ServiceModel.Channels.MessageVersion.Default;
            }
            throw new ArgumentException("version");
        }

        public static MessageVersion GetMessageVersion(string xmltext)
        {
            if (string.IsNullOrEmpty(xmltext))
                return null;
            var root = XElement.Parse(xmltext);
            return GetMessageVersion(root);
        }
        public static MessageVersion GetMessageVersion(XElement root)
        {
            var envelope = root.DescendantsAndSelf().FirstOrDefault(e => e.Name.LocalName == "Envelope");
            if (envelope != null)
            {
                string nsEnv = envelope.Name.NamespaceName;
                var action = envelope.Descendants().FirstOrDefault(e => e.Name.LocalName == "Action");
                if (action != null)
                {
                    string nsAction = action.Name.NamespaceName;
                    if (!string.IsNullOrEmpty(nsEnv) && !string.IsNullOrEmpty(nsAction))
                    {
                        EnvelopeVersion envVersion = null;
                        if (nsEnv == "http://schemas.xmlsoap.org/soap/envelope/")
                            envVersion = EnvelopeVersion.Soap11;
                        else if (nsEnv == "http://www.w3.org/2003/05/soap-envelope")
                            envVersion = EnvelopeVersion.Soap12;
                        else if (nsEnv == "http://schemas.microsoft.com/ws/2005/05/envelope/none")
                            envVersion = EnvelopeVersion.None;

                        if (envVersion != null)
                        {
                            AddressingVersion addrVersion = null;
                            if (nsAction == "http://www.w3.org/2005/08/addressing")
                                addrVersion = AddressingVersion.WSAddressing10;
                            else if (nsAction == "http://schemas.xmlsoap.org/ws/2004/08/addressing")
                                addrVersion = AddressingVersion.WSAddressingAugust2004;
                            else if (nsAction == "http://schemas.microsoft.com/ws/2005/05/addressing/none")
                                addrVersion = AddressingVersion.None;
                            if (addrVersion != null)
                            {
                                return System.ServiceModel.Channels.MessageVersion.CreateVersion(envVersion, addrVersion);
                            }
                        }
                    }
                }
            }
            return null;
        }

        public static Dictionary<string, string> GetNamespaces(string xaml)
        {
            Dictionary<string, string> namespaces = new Dictionary<string, string>();
            using (XmlReader xrd = XmlReader.Create(new StringReader(xaml)))
            {
                XamlXmlReader xxrd = new XamlXmlReader(xrd);
                while (xxrd.Read())
                {
                    if (xxrd.Namespace == null) continue;
                    namespaces.Add(xxrd.Namespace.Prefix, xxrd.Namespace.Namespace);
                }
            }
            return namespaces;
        }

        public static XName GetContractName(string xaml)
        {
            string contractName = null;
            Dictionary<string, string> namespaces = new Dictionary<string, string>();

            StringReader input = new StringReader(xaml);
            using (XmlReader xrd = XmlReader.Create(input))
            {
                XamlXmlReader xxrd = new XamlXmlReader(xrd);
                while (xxrd.Read())
                {
                    if (xxrd.Namespace != null)
                    {
                        namespaces.Add(xxrd.Namespace.Prefix, xxrd.Namespace.Namespace);
                    }
                    else if (xxrd.Type != null && (xxrd.Type.UnderlyingType == typeof(Receive)))
                    {
                        while (xxrd.Read())
                        {
                            if (xxrd.Member != null && xxrd.Member.Name == "ServiceContractName")
                            {
                                xxrd.Read();
                                if (xxrd.NodeType == XamlNodeType.Value && xxrd.Value != null)
                                {
                                    contractName = (string)xxrd.Value;
                                }
                                break;
                            }
                        }
                        break;
                    }
                }
            }

            if (string.IsNullOrEmpty(contractName))
                return null;

            string[] s = contractName.Split(new char[] { ':' }, 2);
            return XName.Get(s.Count() == 2 ? "{" + namespaces[s[0]] + "}" + s[1] : contractName);
        }

        public static string ToString<T>(Dictionary<string, T> dic)
        {
            StringBuilder sb = new StringBuilder("{");
            if (dic != null && dic.Count > 0)
            {
                foreach (var p in dic)
                {
                    sb.AppendFormat("{0} ", p.ToString());
                }
            }
            return sb.ToString().TrimEnd(new char[] { ' ', ',' }) + "}";
        }
        public static string ToString<T>(Dictionary<string, Dictionary<string, T>> dic)
        {
            StringBuilder sb = new StringBuilder("{");
            if (dic != null && dic.Count > 0)
            {
                foreach (var p in dic)
                {
                    sb.AppendFormat("{{{0}, {1}}}, ", p.Key, ToString<T>(p.Value));
                }
            }
            return sb.ToString().TrimEnd(new char[] { ' ', ',' }) + "}";
        }
        public static string ToString<T>(List<T> list)
        {
            StringBuilder sb = new StringBuilder("{");
            if (list != null && list.Count > 0)
            {
                foreach (var p in list)
                {
                    sb.AppendFormat("{0}, ", p.ToString());
                }

            }
            return sb.ToString().TrimEnd(new char[] { ' ', ',' }) + "}";
        }
        public static string ToString<T>(List<Dictionary<string, T>> list)
        {
            StringBuilder sb = new StringBuilder("{");
            if (list != null && list.Count > 0)
            {
                foreach (var p in list)
                {
                    sb.AppendFormat("{0}, ", ToString<T>(p));
                }
            }
            return sb.ToString().TrimEnd(new char[] { ' ', ',' }) + "}";
        }

        public static T DeserializeSection<T>(string config) where T : class
        {
            T cfgSection = Activator.CreateInstance<T>();
            byte[] buffer = new ASCIIEncoding().GetBytes(config.TrimStart(new char[] { '\r', '\n', ' ' }));
            XmlReaderSettings xmlReaderSettings = new XmlReaderSettings();
            xmlReaderSettings.ConformanceLevel = ConformanceLevel.Fragment;

            using (MemoryStream ms = new MemoryStream(buffer))
            {
                using (XmlReader reader = XmlReader.Create(ms, xmlReaderSettings))
                {
                    try
                    {
                        Type cfgType = typeof(ConfigurationSection);
                        MethodInfo mi = cfgType.GetMethod("DeserializeSection", BindingFlags.Instance | BindingFlags.NonPublic);
                        mi.Invoke(cfgSection, new object[] { reader });
                    }
                    catch (Exception ex)
                    {
                        throw new Exception(string.Format("DeserializeSection<{0}> failed, {1}", cfgSection.ToString(), ex.InnerException == null ? ex.Message : ex.InnerException.Message));
                    }
                }
            }
            return cfgSection;
        }
    }

    public class WsdlHelper
    {
        #region GetWsdlMetadata
        public static WsdlImporter GetWsdlMetadata(string url)
        {
            MetadataSet mds = null;
            MetadataExchangeClientMode mode = MetadataExchangeClientMode.MetadataExchange;
            int maxReceivedMessageSize = 3000000;
            Uri address = new Uri(url);

            if (address.Scheme == "file")
            {
                if (File.Exists(url))
                {
                    using (MemoryStream ms = new MemoryStream(File.ReadAllBytes(url)))
                    using (XmlReader rd = XmlReader.Create(ms))
                    {
                        mds = MetadataSet.ReadFrom(rd);
                    }
                }
                else
                    throw new Exception(string.Format("File doesn't exist"));
            }
            else
            {
                System.ServiceModel.Channels.Binding mexBinding = null;
                if (string.Compare(address.Scheme, "http", StringComparison.OrdinalIgnoreCase) == 0)
                    mexBinding = MetadataExchangeBindings.CreateMexHttpBinding();
                else if (string.Compare(address.Scheme, "https", StringComparison.OrdinalIgnoreCase) == 0)
                    mexBinding = MetadataExchangeBindings.CreateMexHttpsBinding();
                else if (string.Compare(address.Scheme, "net.tcp", StringComparison.OrdinalIgnoreCase) == 0)
                    mexBinding = MetadataExchangeBindings.CreateMexTcpBinding();
                else if (string.Compare(address.Scheme, "net.pipe", StringComparison.OrdinalIgnoreCase) == 0)
                    mexBinding = MetadataExchangeBindings.CreateMexNamedPipeBinding();
                else
                    throw new Exception(string.Format("Not supported schema '{0}' for metadata exchange", address.Scheme));

                if (mexBinding is WSHttpBinding)
                {
                    (mexBinding as WSHttpBinding).MaxReceivedMessageSize = maxReceivedMessageSize;
                    mode = MetadataExchangeClientMode.HttpGet;
                }
                else if (mexBinding is CustomBinding)
                    (mexBinding as CustomBinding).Elements.Find<TransportBindingElement>().MaxReceivedMessageSize = maxReceivedMessageSize;

                if (mexBinding != null)
                {
                    MetadataExchangeClient proxy = new MetadataExchangeClient(mexBinding);
                    proxy.ResolveMetadataReferences = true;
                    mds = proxy.GetMetadata(address, mode);
                }
                else
                    throw new Exception(string.Format("Not supported binding for metadata exchange"));
            }
            if (mds != null)
            {
                return new WsdlImporter(mds);
            }

            throw new Exception(string.Format("Cannot read metadaset"));
        }
        #endregion
    }

    // this class is a copy of the internal class System.Activities.Presentation.Internal.PropertyEditing.VisualTreeUtils
    public static class VisualTreeUtils
    {
        // Fields
        private const int MaxSearchDepth = 5;

        // Methods
        public static T FindFocusableElement<T>(T reference) where T : UIElement
        {
            if ((reference == null) || (reference.Focusable && (reference.Visibility == Visibility.Visible)))
            {
                return reference;
            }
            int childrenCount = VisualTreeHelper.GetChildrenCount(reference);
            for (int i = 0; i < childrenCount; i++)
            {
                T child = VisualTreeHelper.GetChild(reference, i) as T;
                if ((child != null) && (child.Visibility == Visibility.Visible))
                {
                    if (child.Focusable)
                    {
                        return child;
                    }
                    child = FindFocusableElement<T>(child);
                    if (child != null)
                    {
                        return child;
                    }
                }
            }
            return default(T);
        }

        public static T FindFocusableParent<T>(UIElement reference) where T : UIElement
        {
            if (reference != null)
            {
                for (UIElement element = VisualTreeHelper.GetParent(reference) as UIElement; element != null; element = VisualTreeHelper.GetParent(element) as UIElement)
                {
                    if (((element.Visibility == Visibility.Visible) && (element is T)) && element.Focusable)
                    {
                        return (element as T);
                    }
                }
            }
            return default(T);
        }

        public static T FindVisualAncestor<T>(DependencyObject child) where T : DependencyObject
        {
            if (child == null)
            {
                return default(T);
            }
            do
            {
                child = VisualTreeHelper.GetParent(child);
            }
            while ((child != null) && !typeof(T).IsAssignableFrom(child.GetType()));
            return (child as T);
        }

        public static DependencyObject GetIndexedVisualParent(DependencyObject child, out int childrenCount, out int childIndex)
        {
            childrenCount = 0;
            DependencyObject parent = VisualTreeHelper.GetParent(child);
            if (parent != null)
            {
                childrenCount = VisualTreeHelper.GetChildrenCount(parent);
                childIndex = 0;
                while (childIndex < childrenCount)
                {
                    if (child.Equals(VisualTreeHelper.GetChild(parent, childIndex)))
                    {
                        return parent;
                    }
                    childIndex++;
                }
            }
            childIndex = -1;
            return null;
        }

        public static T GetNamedChild<T>(DependencyObject container, string name) where T : FrameworkElement
        {
            return GetNamedChild<T>(container, name, 0);
        }

        public static T GetNamedChild<T>(DependencyObject container, string name, int searchDepth) where T : FrameworkElement
        {
            if (((container != null) && !string.IsNullOrEmpty(name)) && (searchDepth >= 0))
            {
                int childrenCount = VisualTreeHelper.GetChildrenCount(container);
                if (childrenCount == 0)
                {
                    return default(T);
                }
                for (int i = 0; i < childrenCount; i++)
                {
                    FrameworkElement child = VisualTreeHelper.GetChild(container, i) as FrameworkElement;
                    if (child != null)
                    {
                        T local1 = child.FindName(name) as T;
                        if (local1 != null)
                        {
                            // added by Roman
                            return local1;
                        }
                        T local = (searchDepth > 0) ? GetNamedChild<T>(child, name, searchDepth - 1) : default(T);
                        if (local != null)
                        {
                            return local;
                        }
                    }
                }
            }
            return default(T);
        }

        public static T GetTemplateChild<T>(DependencyObject element) where T : DependencyObject
        {
            for (int i = 5; (i > 0) && (element != null); i--)
            {
                if (VisualTreeHelper.GetChildrenCount(element) < 1)
                {
                    return default(T);
                }
                element = VisualTreeHelper.GetChild(element, 0);
                T local = element as T;
                if (local != null)
                {
                    return local;
                }
            }
            return default(T);
        }

        public static bool IsVisible(UIElement element)
        {
            for (UIElement element2 = element; element2 != null; element2 = VisualTreeHelper.GetParent(element2) as UIElement)
            {
                if (element2.Visibility != Visibility.Visible)
                {
                    return false;
                }
            }
            return true;
        }
    }

    [DataContract]
    public class SendMessageInspector : ISendMessageCallback
    {
        public dynamic MessageHeaders { get; set; }

        void ISendMessageCallback.OnSendMessage(OperationContext operationContext)
        {
            if (this.MessageHeaders is List<MessageHeader>)
            {
                foreach (MessageHeader header in this.MessageHeaders)
                {
                    operationContext.OutgoingMessageHeaders.Add(header);
                }
            }
            else if (this.MessageHeaders is MessageHeader)
            {
                operationContext.OutgoingMessageHeaders.Add(this.MessageHeaders);
            }
            else if (this.MessageHeaders is XElement)
            {
                if (string.Compare(this.MessageHeaders.Name.LocalName, "Header", true) == 0)
                {
                    foreach (var eh in this.MessageHeaders.Elements())
                    {
                        operationContext.OutgoingMessageHeaders.Add(this.CreateHeader(eh));
                    }
                }
                else
                {
                    operationContext.OutgoingMessageHeaders.Add(this.CreateHeader(this.MessageHeaders));
                }
            }
            else
                throw new Exception("Wrong MessageHeader type");
        }

        private MessageHeader CreateHeader(XElement element)
        {
            XAttribute amu = element.Attribute("mustUnderstand");
            XAttribute aa = element.Attribute("actor");
            bool bMustUnderstand = amu == null ? false : Convert.ToBoolean(Convert.ToInt32(amu.Value));
            string actor = aa == null ? string.Empty : aa.Value;
            return MessageHeader.CreateHeader(element.Name.LocalName, element.Name.Namespace.NamespaceName, element.Value, bMustUnderstand, actor);
        }
    }
}
